Namespace.Use("Locale.Management", function(ns) {
    //
    //  Imports.
    //
    var GetLanguageCode = Namespace.Import("Browser.Language.GetLanguageCode");
    var LocalStorage = Namespace.Import("Browser.LocalStorage");
    var MessageTree = Namespace.Import("Locale.Tree.MessageTree");
    var Loader = Namespace.Import("Loader");

    //
    //  Global.
    //
    var g_Trees = [];
    var g_CurrentLanguageID = GetLanguageCode();
    var g_FallbackLanguageID = "en-US";

    //
    //  Public APIs.
    //
    var AccessCurrentLanguageID = Polymorphic.Create(
        [],
        function() {
            return g_CurrentLanguageID;
        },
        [String],
        function(language_id) {
            g_CurrentLanguageID = language_id;
        }
    );
    var AccessFallbackLanguageID = Polymorphic.Create(
        [],
        function() {
            return g_FallbackLanguageID;
        },
        [String],
        function(language_id) {
            g_FallbackLanguageID = language_id;
        }
    );
    var LoadCurrentLanguageID = function() {
        if (!LocalStorage.IsAvailable()) {
            console.warn("Local storage is not available.");
            return;
        }
        g_CurrentLanguageID = LocalStorage.GetValue(
            "XJWFM_CURRENT_LANGUAGE_ID",
            g_CurrentLanguageID
        );
    };
    var SaveCurrentLanguageID = function() {
        if (!LocalStorage.IsAvailable()) {
            console.warn("Local storage is not available.");
            return;
        }
        LocalStorage.SetValue("XJWFM_CURRENT_LANGUAGE_ID", g_CurrentLanguageID);
    };
    var MergeMessageTree = Polymorphic.Create(
        [MessageTree],
        function(tree) {
            g_Trees.push(tree);
        }
    );
    var LoadMessage = Polymorphic.Create(
        [String, String, String, Boolean],
        function(language_id, key_path, default_value, allow_fallback) {
            for (var i = 0; i < g_Trees.length; ++i) {
                var msg_tree = g_Trees[i];
                if (!msg_tree.IsLanguageMatches(language_id)) {
                    continue;
                }
                if (msg_tree.HasMessage(key_path)) {
                    return msg_tree.GetMessage(key_path);
                }
            }

            if (allow_fallback) {
                return LoadMessage(g_FallbackLanguageID, key_path, default_value, false);
            } else {
                if (default_value !== null) {
                    return default_value;
                }
                throw "No such key.";
            }
        },
        [String, String],
        function(key_path, default_value) {
            return LoadMessage(g_CurrentLanguageID, key_path, default_value, true);
        },
        [String],
        function(key_path) {
            return LoadMessage(key_path, null);
        }
    );

    //  Load current language from local storage immediately.
    LoadCurrentLanguageID();

    //
    //  Export public APIs.
    //
    Namespace.Export(ns, "AccessCurrentLanguageID", AccessCurrentLanguageID);
    Namespace.Export(ns, "AccessFallbackLanguageID", AccessFallbackLanguageID);
    Namespace.Export(ns, "LoadCurrentLanguageID", LoadCurrentLanguageID);
    Namespace.Export(ns, "SaveCurrentLanguageID", SaveCurrentLanguageID);
    Namespace.Export(ns, "MergeMessageTree", MergeMessageTree);
    Namespace.Export(ns, "LoadMessage", LoadMessage);
});
